/* 
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. 
 * 
 * This file is available and licensed under the following license:
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice, 
 *     this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *
 *   * Neither the name of Sun Microsystems nor the names of its contributors 
 *     may be used to endorse or promote products derived from this software 
 *     without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package effectsplayground;

import javafx.scene.*;
import javafx.scene.effect.*;
import javafx.scene.effect.light.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.text.*;
import javafx.stage.*;
import javafx.util.*;
import java.lang.Math.*;
import effectsplayground.control.*;
import effectsplayground.Main.Preview;

/**
 * @author campbell
 */

package class EffectControl {
    public var previewNode:Preview;
    public var controlsGroup:Node;

    public var canvasEffect:Effect;
    public var canvasImage:Node = ImageView {
        smooth: true
        effect: bind canvasEffect
        image: bind Main.fgImage
    };

    public var thumbEffect:Effect;
    public var thumbImage:Node = ImageView {
        effect: bind thumbEffect
        image: bind Main.fgThumb
    }
}

class ModeButton extends RadioButton {
    var mode:BlendMode = BlendMode.ADD;
}

package class BlendControl extends EffectControl {
    var slider:LabeledSlider;
    def bg = ButtonGroup { };

    override public var canvasImage = Group {
        var selMode = bind (bg.selectedButton as ModeButton).mode;
        blendMode: bind if (selMode != null) selMode else BlendMode.MULTIPLY
        content: [
            ImageView {
                smooth: true
                translateX: bind (Main.fgImage.width/2) - (Main.bgImage.width/2)
                translateY: bind (Main.fgImage.height/2) - (Main.bgImage.height/2)
                image: bind Main.bgImage
            }
            ImageView {
                smooth: true
                image: bind Main.fgImage
                opacity: bind slider.value
            },
        ]
    };

    override public var thumbImage = Group {
        blendMode: BlendMode.MULTIPLY
        content: [
            ImageView {
                translateX: bind (Main.fgImage.width/2) - (Main.bgImage.width/2)
                translateY: bind (Main.fgImage.height/2) - (Main.bgImage.height/2)
                image: bind Main.bgThumb
            },
            ImageView {
                image: bind Main.fgThumb
                opacity: 0.5
            }
        ]
    };

    override public var controlsGroup = Group {
        content: [
            slider = LabeledSlider {
                translateX: 15
                translateY: 22
                text: "Opacity"
                value: 0.5
                padding: 90
            },
            ModeButton {
                translateX: 290
                translateY: 15
                bg: bind bg
                text: "Multiply"
                mode: BlendMode.MULTIPLY
                selected: true
            },
            ModeButton {
                translateX: 290
                translateY: 35
                bg: bind bg
                text: "Screen"
                mode: BlendMode.SCREEN
            },
            ModeButton {
                translateX: 370
                translateY: 15
                bg: bind bg
                text: "Overlay"
                mode: BlendMode.OVERLAY
            },
            ModeButton {
                translateX: 370
                translateY: 35
                bg: bind bg
                text: "Hard Light"
                mode: BlendMode.HARD_LIGHT
            },
            ModeButton {
                translateX: 460
                translateY: 15
                bg: bind bg
                text: "Difference"
                mode: BlendMode.DIFFERENCE
            },
            ModeButton {
                translateX: 460
                translateY: 35
                bg: bind bg
                text: "Exclusion"
                mode: BlendMode.EXCLUSION
            },
        ]
    };
}

package class BlurControl extends EffectControl {
    var slider:LabeledSlider;
    
    override public var canvasEffect = GaussianBlur {
        radius: bind if (slider.value < 1) 1 else slider.value
    };

    override public var thumbEffect = GaussianBlur { radius: 5 };

    override public var controlsGroup = Group {
        content: [
            slider = LabeledSlider {
                translateX: 15
                translateY: 22
                text: "Radius"
                minimum: 1
                maximum: 63
                value: 10
                padding: 90
            }
        ]
    };
}

package class MotionBlurControl extends EffectControl {
    var slider:LabeledSlider;
    var knob:LabeledKnob;

    override public var canvasEffect = MotionBlur {
        radius: bind if (slider.value < 1) 1 else slider.value
        angle: bind knob.value
    };

    override public var thumbEffect = MotionBlur { radius: 5 angle: 45 };

    override public var controlsGroup = Group {
        content: [
            slider = LabeledSlider {
                translateX: 15
                translateY: 22
                text: "Radius"
                minimum: 1
                maximum: 63
                value: 10
                padding: 90
            },
            knob = LabeledKnob {
                translateX: 320
                translateY: 20
                text: "Angle"
                minimum:  -90.0
                maximum:   90.0
                value:     45.0
                minAngle: -90.0
                maxAngle:  90.0
            }
        ]
    };
}

package class BloomControl extends EffectControl {
    var slider:LabeledSlider;

    override public var canvasEffect = Bloom {
        threshold: bind slider.value
    };

    override public var thumbEffect = Bloom { threshold: 0.0 };

    override public var controlsGroup = Group {
        content: [
            slider = LabeledSlider {
                translateX: 15
                translateY: 22
                text: "Threshold"
                value: 0.0
            }
        ]
    };
}

package class GlowControl extends EffectControl {
    var slider:LabeledSlider;

    override public var canvasEffect = Glow {
        level: bind slider.value
    };

    override public var thumbEffect = Glow { level: 0.7 };

    override public var controlsGroup = Group {
        content: [
            slider = LabeledSlider {
                translateX: 15
                translateY: 22
                text: "Level"
                value: 0.7
                padding: 90
            }
        ]
    };
}

package class ColorAdjustControl extends EffectControl {
    var bslider:LabeledSlider;
    var cslider:LabeledSlider;
    var hslider:LabeledSlider;
    var sslider:LabeledSlider;

    override public var canvasEffect = ColorAdjust {
        brightness: bind bslider.value
        contrast:   bind if (cslider.value < 0.25) 0.25 else cslider.value
        hue:        bind hslider.value
        saturation: bind sslider.value
    };

    override public var thumbEffect = ColorAdjust { hue: -1 };

    override public var controlsGroup = Group {
        content: [
            bslider = LabeledSlider {
                translateX: 15
                translateY: 12
                text: "Brightness"
                minimum: -1
                maximum:  1
                value:    0
            },
            cslider = LabeledSlider {
                translateX: 15
                translateY: 32
                text: "Contrast"
                minimum: 0.25
                maximum: 4.00
                value:   1.00
            },
            hslider = LabeledSlider {
                translateX: 290
                translateY: 12
                text: "Hue"
                minimum: -1
                maximum:  1
                value:   -1
            },
            sslider = LabeledSlider {
                translateX: 290
                translateY: 32
                text: "Saturation"
                minimum: -1
                maximum:  1
                value:    0
            },
        ]
    };
}

package class ShadowControl extends EffectControl {
    def width = 80;
    def pad = 85;
    var rslider:LabeledSlider;
    var xslider:LabeledSlider;
    var yslider:LabeledSlider;
    var picker:ColorPicker;

    override public var thumbImage = ImageView {
        effect: bind thumbEffect
        image: bind Main.fgThumb
        x: 0
        y: 6
        fitHeight: bind Main.fgThumb.height*0.7
        preserveRatio: true
    }

    override public var controlsGroup = Group {
        content: [
            rslider = LabeledSlider {
                translateX: 15
                translateY: 12
                text: "Radius"
                minimum:  1
                maximum: 63
                value:   10
                width: bind width
                padding: bind pad
            },
            xslider = LabeledSlider {
                translateX: 195
                translateY: 12
                text: "X Offset"
                minimum: -20
                maximum:  20
                value:     4
                width: bind width
                padding: bind pad
            },
            yslider = LabeledSlider {
                translateX: 375
                translateY: 12
                text: "Y Offset"
                minimum: -20
                maximum:  20
                value:     4
                width: bind width
                padding: bind pad
            },
            Text {
                translateX: 15
                translateY: 44
                content: "Color"
                font: Font { size: 10 }
                fill: Color.WHITE
            },
            picker = ColorPicker {
                translateX: 60
                translateY: 32
            }
        ]
    };
}

package class DropShadowControl extends ShadowControl {
    override public var canvasEffect = DropShadow {
        radius: bind if (rslider.value < 1) 1 else rslider.value
        offsetX: bind xslider.value
        offsetY: bind yslider.value
        color: bind if (picker.selectedColor != null) picker.selectedColor else Color.BLACK
    };

    override public var thumbEffect = DropShadow { spread: 0.2 };
}

package class InnerShadowControl extends ShadowControl {
    override public var canvasEffect = InnerShadow {
        radius: bind if (rslider.value < 1) 1 else rslider.value
        offsetX: bind xslider.value
        offsetY: bind yslider.value
        color: bind if (picker.selectedColor != null) picker.selectedColor else Color.BLACK
    };

    override public var thumbEffect = InnerShadow { choke: 0.2 };
}

package class PerspectiveControl extends EffectControl {
    var slider:LabeledSlider;

    function getTransform(angle:Number, imgWidth:Number, imgHeight:Number, yoff:Number) : PerspectiveTransform {
        PerspectiveTransform {
            var ox     = 0;
            var oy     = yoff;
            var pw     = imgWidth;
            var ph     = imgHeight;
            var radius = pw/2;
            var back   = ph/10;
            var t      = toRadians(angle);

            ulx: ox+radius-sin(t)*radius   uly:oy+0 -cos(t)*back
            urx: ox+radius+sin(t)*radius   ury:oy+0 +cos(t)*back
            lrx: ox+radius+sin(t)*radius   lry:oy+ph-cos(t)*back
            llx: ox+radius-sin(t)*radius   lly:oy+ph+cos(t)*back
        }
    }

    override public var canvasEffect =
        bind getTransform(slider.value, Main.fgImage.width, Main.fgImage.height, 0);

    override public var thumbEffect =
        bind getTransform(30.0, Main.fgThumb.width*0.7, Main.fgThumb.height*0.7, 7);

    override public var controlsGroup = Group {
        content: [
            slider = LabeledSlider {
                translateX: 15
                translateY: 22
                text: "Angle"
                value:    60.0
                minimum:   0.0
                maximum: 360.0
                padding: 90
            }
        ]
    };
}

package class LightingControl extends EffectControl {
    def width = 60;

    var dcSlider:LabeledSlider;
    var ssSlider:LabeledSlider;
    var scSlider:LabeledSlider;
    var seSlider:LabeledSlider;
    var aKnob:LabeledKnob;
    var eKnob:LabeledKnob;

    override public var canvasEffect = Lighting {
        light: DistantLight {
            azimuth: bind aKnob.value
            elevation: bind eKnob.value
        }
        surfaceScale: bind ssSlider.value
        diffuseConstant: bind dcSlider.value
        specularConstant: bind scSlider.value
        specularExponent: bind seSlider.value
    };

    override public var thumbEffect = Lighting { };

    override public var controlsGroup = Group {
        content: [
            dcSlider = LabeledSlider {
                translateX: 15
                translateY: 12
                text: "Diffuse"
                value:   1.0
                minimum: 0.0
                maximum: 2.0
                width: bind width
                padding: 75
            },
            ssSlider = LabeledSlider {
                translateX: 15
                translateY: 32
                text: "Scale"
                value:    1.5
                minimum:  0.0
                maximum: 10.0
                width: bind width
                padding: 75
            },
            scSlider = LabeledSlider {
                translateX: 170
                translateY: 12
                text: "Specular Con"
                value:   0.3
                minimum: 0.0
                maximum: 2.0
                width: bind width
                padding: 110
            },
            seSlider = LabeledSlider {
                translateX: 170
                translateY: 32
                text: "Specular Exp"
                value:   20.0
                minimum:  0.0
                maximum: 40.0
                width: bind width
                padding: 110
            },
            aKnob = LabeledKnob {
                translateX: 395
                translateY: 20
                text: "Azimuth"
                value:     45.0
                minimum:    0.0
                maximum:  360.0
                minAngle:   0.0
                maxAngle: 360.0
            },
            eKnob = LabeledKnob {
                translateX: 480
                translateY: 20
                text: "Elevation"
                value:     45.0
                minimum:    0.0
                maximum:   90.0
                minAngle: -90.0
                maxAngle:   0.0
            },
        ]
    };
}

package class SepiaControl extends EffectControl {
    var slider:LabeledSlider;
    override public var canvasEffect = SepiaTone {
        level: bind slider.value
    };

    override public var thumbEffect = SepiaTone { };

    override public var controlsGroup = Group {
        content: [
            slider = LabeledSlider {
                translateX: 15
                translateY: 22
                text: "Level"
                value: 1.0
                padding: 90
            }
        ]
    };
}

package class ReflectionControl extends EffectControl {
    var fractionSlider:LabeledSlider;
    var topOffsetSlider:LabeledSlider;
    var topOpacitySlider:LabeledSlider;
    var botOpacitySlider:LabeledSlider;
    def width = 120;

    override public var canvasEffect = Reflection {
        fraction:      bind fractionSlider.value
        topOffset:     bind topOffsetSlider.value
        topOpacity:    bind topOpacitySlider.value
        bottomOpacity: bind botOpacitySlider.value
    };

    override public var canvasImage = ImageView {
        effect: bind canvasEffect
        image: bind Main.fgImage
        smooth: true
        fitHeight: bind Main.fgImage.height*0.7
        preserveRatio: true
    };

    override public var thumbEffect = Reflection { topOffset: 1 fraction: 0.4 }

    override public var thumbImage = ImageView {
        effect: bind thumbEffect
        image: bind Main.fgThumb
        x: 0
        y: 3
        fitHeight: bind Main.fgThumb.height*0.6
        preserveRatio: true
    }

    override public var controlsGroup = Group {
        content: [
            fractionSlider = LabeledSlider {
                translateX: 15
                translateY: 12
                text: "Fraction"
                value: 0.4
                width: bind width
            },
            topOffsetSlider = LabeledSlider {
                translateX: 15
                translateY: 32
                text: "Top Offset"
                minimum:  0.0
                maximum: 20.0
                value:    3.0
                width: bind width
            },
            topOpacitySlider = LabeledSlider {
                translateX: 260
                translateY: 12
                text: "Top Opacity"
                value: 0.5
                padding: 130
                width: bind width
            },
            botOpacitySlider = LabeledSlider {
                translateX: 260
                translateY: 32
                text: "Bottom Opacity"
                value: 0.0
                padding: 130
                width: bind width
            },
        ]
    };
}

function run() {

var ec = LightingControl { };
Stage {
    width: 600
    height: 300
    scene: Scene {
        fill: Color.BLACK
        content: [
            ec.controlsGroup
        ]
    }
}
}
